!> author: 左志华
!> date: 2022-05-05
!>
!> Easy string <br>
!> 字符串操作
module string_m

    use, intrinsic :: iso_c_binding, only: c_null_char, newline => c_new_line
    use, intrinsic :: iso_fortran_env, only: sp => real32, dp => real64
    implicit none
    private

    public :: to_string, to_lower, f_c_string, progress_bar, puts, newline, starts_with

    character(*), parameter :: uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    character(*), parameter :: lowercase = "abcdefghijklmnopqrstuvwxyz"

contains

    !> Make a string lowercase <br>
    !> 将字符串转为小写
    pure function to_lower(x, reverse) result(y)
        character(*), intent(in) :: x               !! Input string <br>
                                                    !! 输入字符串
        logical, intent(in), optional :: reverse    !! Whether to convert to uppercase <br>
                                                    !! 是否转为大写
        character(len(x)) :: y
        integer :: i, k

        if (present(reverse)) then
            if (reverse) then

                do concurrent(i=1:len(x))
                    k = index(lowercase, x(i:i))
                    if (k > 0) then
                        y(i:i) = uppercase(k:k)
                    else
                        y(i:i) = x(i:i)
                    end if
                end do
                return

            end if
        end if

        do concurrent(i=1:len(x))
            k = index(uppercase, x(i:i))
            if (k > 0) then
                y(i:i) = lowercase(k:k)
            else
                y(i:i) = x(i:i)
            end if
        end do

    end function to_lower

    !> Format other types to a string <br>
    !> 将其他类型转化为字符串
    pure function to_string(x, fmt) result(s)
        class(*), intent(in) :: x                   !! Input object <br>
                                                    !! 输入类型
        character(*), intent(in), optional :: fmt   !! format string <br>
                                                    !! 格式化字符串
        character(:), allocatable :: s
        character(128) :: s_

        if (present(fmt)) s = "("//fmt//")"
        select type (x)
        type is (integer)
            if (present(fmt)) then
                write (s_, s) x
            else
                write (s_, *) x
            end if
            s = trim(s_)
        type is (logical)
            if (present(fmt)) then
                write (s_, s) x
                s = trim(s_)
            else
                if (x) then
                    s = "T"
                else
                    s = "F"
                end if
            end if
        type is (real(sp))
            if (present(fmt)) then
                write (s_, s) x
            else
                write (s_, *) x
            end if
            s = trim(s_)
        type is (real(dp))
            if (present(fmt)) then
                write (s_, s) x
            else
                write (s_, *) x
            end if
            s = trim(s_)
        class default
            s = "[*]"
        end select

    end function to_string

    !> Convert Fortran string to C string <br>
    !> 将 Fortran 字符串转换为 C 字符串
    pure function f_c_string(f_string, reverse) result(c_string)
        character(*), intent(in) :: f_string            !! Fortran string <br>
                                                        !! Fortran 字符串
        logical, intent(in), optional :: reverse        !! Whether to convert to Fortran string <br>
                                                        !! 是否转换为 Fortran 字符串
        character(:), allocatable :: c_string

        if (present(reverse)) then
            if (reverse) then
                associate (len => len(f_string))
                if (f_string(len:len) == c_null_char) then
                    c_string = f_string(1:len - 1)
                else
                    c_string = f_string
                end if
                end associate
                return
            end if
        end if

        c_string = f_string//c_null_char
    end function f_c_string

    !> Generate a progress bar string <br>
    !> 生成一个进度条字符串
    pure function progress_bar(msg, p) result(bar)
        character(*), intent(in) :: msg     !! progress bar description <br>
                                            !! 进度条描述
        real, intent(in) :: p               !! current progress percentage <br>
                                            !! 当前进度百分比
        character(:), allocatable :: bar
        integer :: length_, l
        parameter(l=35)

        length_ = len(msg) + l + 11
        allocate (character(length_) :: bar)
        write (bar, "(A,A2,A35,A2,F5.1,A2)") msg, &
            " [", repeat("#", nint(p*l))//repeat(".", l - nint(p*l)), "] ", 100*p, " %"

    end function progress_bar

    !> Output string without wrapping <br>
    !> 不换行输出字符串
    impure subroutine puts(line)
        character(*), intent(in) :: line    !! Output string <br>
                                            !! 输出字符串
        write (*, '(a)', advance='no') line
    end subroutine puts

    !> Determines whether the string starts with a string; if reverse is true,
    !> search from back to front (ends_with) <br>
    !> 判断字符串是否以某个字符串开头; 如果 reverse 为真, 则从后往前查找 (ends_with)
    pure logical function starts_with(string, substring, reverse) result(ans)
        character(*), intent(in) :: string      !! main string <br>
                                                !! 主字符串
        character(*), intent(in) :: substring   !! substring <br>
                                                !! 子字符串
        logical, intent(in), optional :: reverse!! whether to start the comparison from the reverse <br>
                                                !! 是否从后面开始比较

        if (present(reverse)) then
            if (reverse) then
                ans = index(string, substring, .true.) + len(substring) - 1 == len(string)
                return
            end if
        end if

        ans = index(string, substring) == 1

    end function starts_with

end module string_m
